#include <math.h>
#include "Game.h"

/**
 * 构造方法。
 * @param bitmap    后台缓冲位图
 * @param gameBoard 游戏区域
 */
Game::Game(QPixmap* bitmap, QWidget* gameBoard) :
    mBitmap(bitmap),
    mDifficulty(EASY),
    mGameBoard(gameBoard),
    mImage(Const::GAME_BOARD_WIDTH, Const::GAME_BOARD_HEIGHT, QImage::Format_RGB888),
    mPicHasBorder(true),
    mStatus(END) {
    // 初始化成员
    memset(mPictures, 0, sizeof(mPictures));

    // 设置图片
    this->setPicture(Const::PICTURE_FILE_PATH);

    // 置随机数种子
    srand(static_cast<uint>(time(nullptr)));
}

/**
 * 析构方法。
 */
Game::~Game() {
    this->deletePictures();
}

/**
 * 创建所有图片控件。
 */
void Game::createPictures() {
    int left, top;                                                              // 图片控件位置
    int baseCount = static_cast<int>(sqrt(static_cast<double>(mDifficulty)));   // 一行（列）的图片控件数
    int size = Const::EMPTY_AREA_WIDTH / baseCount;                             // 图片控件尺寸
    QRect rect;                                                                 // 图片控件位置、尺寸
    int j = 0;                                                                  // 换行标志，0为第一行，1为第二行，依次类推

    // 删除所有图片
    this->deletePictures();

    for (int i = 0; i < mDifficulty; i++) {
        if (i % baseCount == 0 && i != 0) { // 如果一行满了，换到下一列
            j++;
        }

        // 计算左上角坐标
        left = Const::EMPTY_AREA_X + size * (i % baseCount);
        top = Const::EMPTY_AREA_Y + size * j;

        // 设置位置、尺寸
        rect.setRect(left, top, size, size);

        // 创建图片控件
        mPictures[i] = new PictureCtrl(mPicHasBorder, &mImage, mGameBoard, &rect);
    }
}

/**
 * 删除所有图片控件。
 */
void Game::deletePictures() {
    for (int i = 0; i < Const::MAX_PICTURE_CTRL_COUNT; i++) {
        if (mPictures[i] == nullptr) {
            break;
        }
        delete mPictures[i];
        mPictures[i] = nullptr;
    }
}

/**
 * 游戏结束。
 */
void Game::gameOver() {
    // 将状态设置为已结束
    mStatus = END;

    // 发送通知
    emit UM_GAME_OVER();
}

/**
 * 获取游戏难度。
 * @return 游戏难度
 */
Game::Difficulty Game::getDifficulty() {
    return mDifficulty;
}

/**
 * 获取游戏状态。
 * @return 游戏状态
 */
Game::Status Game::getStatus() {
    return mStatus;
}

/**
 * 判断是否胜利。
 * @return 胜利则返回 true，否则返回 false
 */
bool Game::isWon() {
    bool result = false;
    for (int i = 0; i < mDifficulty; i++) {
        result = mPictures[i]->isOnOriginalPosition();
        if (!result) {
            break;
        }
    }
    return result;
}

/**
 * 移动图片控件。
 * @param pictureCtrl   图片控件
 */
void Game::movePicture(QWidget* pictureCtrl) {
    if (mStatus != PLAYING) {
        return;
    }

    QPoint position = pictureCtrl->pos();

    // 如果要移动的图片控件与空点相邻，则移动
    if ((position.y() == mEmptyPoint.y() && abs(position.x() - mEmptyPoint.x()) == pictureCtrl->width()) ||
        (position.x() == mEmptyPoint.x() && abs(position.y() - mEmptyPoint.y()) == pictureCtrl->height())) {
        pictureCtrl->move(mEmptyPoint);
        mEmptyPoint = position;

        // 判断是否胜利
        if (this->isWon()) {
            this->gameOver();
        }
    }
}

/**
 * 获取图片控件是否有边框标志。
 * @return 有边框则为 true，否则为 false
 */
bool Game::pictureHasBorder() {
    return mPicHasBorder;
}

/**
 * 设置游戏难度。
 * @param diff  游戏难度
 */
void Game::setDifficulty(Game::Difficulty diff) {
    // 设置难度
    mDifficulty = diff;

    // 重新创建所有图片
    this->createPictures();

    // 将状态设置为已结束
    mStatus = END;
}

/**
 * 设置图片。
 * @param filePath    图片文件路径
 */
void Game::setPicture(const char* filePath) {
    // 将图片画到游戏区域上
    QImage image(filePath);
    QPainter drawer;
    QRect srcRect(0, 0, Const::GAME_BOARD_WIDTH, Const::GAME_BOARD_HEIGHT);
    QRect dstRect(0, 0, image.width(), image.height());
    drawer.begin(&mImage);
    drawer.drawImage(srcRect, image, dstRect);
    drawer.end();
    mDrawer.begin(mBitmap);
    mDrawer.drawImage(0, 0, mImage);

    // 画中间的空白区域
    mDrawer.fillRect(Const::EMPTY_AREA_X, Const::EMPTY_AREA_Y,
        Const::EMPTY_AREA_WIDTH, Const::EMPTY_AREA_HEIGHT, Const::EMPTY_AREA_COLOR);
    mDrawer.end();

    // 通知游戏区域重绘
    mGameBoard->update();

    // 创建所有图片
    this->createPictures();

    // 将状态设置为已结束
    mStatus = END;
}

/**
 * 设置图片控件是否有边框。
 * @param hasBorder 有边框则为 true，否则为 false
 */
void Game::setPictureBorder(bool hasBorder) {
    mPicHasBorder = hasBorder;

    // 设置所有图片控件的边框
    for (int i = 0; i < mDifficulty; i++) {
        mPictures[i]->setBorder(hasBorder);
    }
}

/**
 * 游戏开始。
 */
void Game::start() {
    // 重新创建所有图片
    if (mStatus != END) {
        this->createPictures();
    }

    // 重置成员变量
    int baseCount = static_cast<int>(sqrt(static_cast<double>(mDifficulty)));
    mEmptyPoint.setX(Const::EMPTY_AREA_X + mPictures[0]->width() * baseCount);
    mEmptyPoint.setY(Const::EMPTY_AREA_Y + mPictures[0]->height() * (baseCount - 1));
    mStatus = PLAYING;

    // 右下角图片控件右移
    QPoint position = mPictures[mDifficulty - 1]->pos();
    mPictures[mDifficulty - 1]->move(mEmptyPoint);
    mEmptyPoint = position;

    // 乱序其它图片控件的位置
    int index1, index2;
    for (int i = 0; i < mDifficulty - 1; i++) {
        do {
            index1 = rand() % (mDifficulty - 1);
            index2 = rand() % (mDifficulty - 1);
        } while (index1 == index2);
        this->swapPictures(index1, index2);
    }
}

/**
 * 交换两个图片控件的位置。
 * @param index1    图片控件1的索引
 * @param index2    图片控件2的索引
 */
void Game::swapPictures(int index1, int index2) {
    QPoint position1 = mPictures[index1]->pos();
    QPoint position2 = mPictures[index2]->pos();
    mPictures[index1]->move(position2);
    mPictures[index2]->move(position1);
}
